<?php

namespace app\common\model\banxi\sys;

use app\common\enums\UserConstantsEnum;
use app\common\enums\UserStatusEnum;
use app\common\model\common\BaseModel;
use think\exception\ValidateException;
use think\facade\Cache;
use think\model\concern\SoftDelete;

/**
 * Admin模型
 * @property int $id              管理员ID
 * @property string $username        管理员用户名
 * @property string $nickname        管理员昵称
 * @property string $email           管理员邮箱
 * @property string $mobile          管理员手机号
 * @property string $last_login_ip   上次登录IP
 * @property string $last_login_time 上次登录时间
 * @property int $login_failure   登录失败次数
 * @property string $password        密码密文
 * @property string $salt            密码盐（废弃待删）
 * @property string $status          状态:enable=启用,disable=禁用,...(string存储，可自定义其他)
 */
class SysUser extends BaseModel
{
    use SoftDelete;

    protected $name = 'banxi_sys_user';
    /**
     * @var string 自动写入时间戳
     */
    protected $autoWriteTimestamp = true;

    protected $deleteTime = 'del';

    protected $defaultSoftDelete = 0;

    /**
     * 登录错误次数
     */
    public const LOGIN_ERROR_NUMBER = 5;
    /**
     * 登录错误次数
     */
    public const LOGIN_ERROR_LIMIT_TIME = 60 * 10;
    /**
     * 追加属性
     */
    protected $append = [
        'statusCn',
        'admin',
        'name'
    ];

    protected $type = [
        'id' => 'string',
        'del' => 'string',
        'roleId' => 'string',
        'createTime' => 'timestamp:Y-m-d H:i:s',
        'updateTime' => 'timestamp:Y-m-d H:i:s',
    ];

    //-----------------------------------------------------------------------------------
    //｜                                   获取器                                        ｜
    //-----------------------------------------------------------------------------------

    public function getStatusCnAttr($value, $data)
    {
        $value = $value ?: ($data['status'] ?? '');
        return (new UserStatusEnum())->getDescription($value) ?? '';
    }

    public function getAdminAttr($value, $data)
    {
        return UserConstantsEnum::ROLE_ID == $data['roleId'];
    }

    public function getNameAttr($value, $data)
    {
        return $data['userName'];
    }

    //-----------------------------------------------------------------------------------
    //｜                                   处理方法                                        ｜
    //-----------------------------------------------------------------------------------

    public function getLimitKey($username, $type)
    {
        return 'login_error' . $type . ':' . $username;
    }

    //验证登录次数
    public function validateLimit($username, $type): bool
    {
        $key = $this->getLimitKey($username, $type);
        $errorNumber = Cache::get($key);
        if ($errorNumber == self::LOGIN_ERROR_NUMBER) {
            if ($type == 'sys_user') {
                //存日志
                $sysLogininfor = new SysLogininfor();
                $sysLogininfor->recordLogininfor(null, $username, $type, SysLogininfor::LONGIN_FAIl, '登录失败，已被锁定');
            }
            return false;
        }
        return true;
    }

    public function clearLimit($username, $type)
    {
        $key = $this->getLimitKey($username, $type);
        Cache::delete($key);
        return true;
    }

    public function recordError($checkLogin, $username, $userType, $msgBegin)
    {
        if ($checkLogin) {
            $this->clearLimit($username, $userType);
            return true;
        }
        $key = $this->getLimitKey($username, $userType);
        $errorNum = Cache::get($key);
        $errorNum = is_null($errorNum) ? 1 : $errorNum + 1;
        $sysLogininfor = new SysLogininfor();
        if ($errorNum == self::LOGIN_ERROR_NUMBER) {
            if ($userType == 'sys_user') {
                //存日志
                $sysLogininfor->recordLogininfor(null, $username, $userType, SysLogininfor::LONGIN_FAIl, '用户名或密码错误次数多，已被锁定');
            }
            Cache::set($key, $errorNum, self::LOGIN_ERROR_LIMIT_TIME);
            throw new ValidateException(sprintf('登录失败次数已满，请%s分钟后再试', self::LOGIN_ERROR_LIMIT_TIME / 60));
        } else {
            if ($userType == 'sys_user') {
                //存日志
                $sysLogininfor->recordLogininfor(null, $username, $userType, SysLogininfor::LONGIN_FAIl, '用户名或密码错误');
            }
            Cache::set($key, $errorNum);
            throw new ValidateException(sprintf(
                '%s，您已错误：%d次，%d次后将锁定%d分钟',
                $msgBegin,
                $errorNum,
                self::LOGIN_ERROR_NUMBER,
                self::LOGIN_ERROR_LIMIT_TIME / 60
            ));
        }
    }

    /**
     * 重置用户密码
     * @param int|string $uid 管理员ID
     * @param string $newPassword 新密码
     * @return int|SysUser
     */
    public function resetPassword(int|string $uid, string $newPassword): int|SysUser
    {
        return $this->where(['id' => $uid])->update(['password' => hash_password($newPassword), 'salt' => '']);
    }

    public function updateUser($user)
    {
        if (!empty($user['phone']) && $this->checkPhoneUnique($user['phone'], $user['id'])) {
            throw new ValidateException('修改用户失败，手机号码已存在');
        } else if (!empty($user['userName']) && $this->checkUserNameUnique($user['userName'], $user['id'])) {
            throw new ValidateException('修改用户失败，账号已存在');
        }
        return $this->update($user, ['id' => $user['id']]);
    }

    public function saveUser($user)
    {
        if (!empty($user['phone']) && $this->checkPhoneUnique($user['phone'], null)) {
            throw new ValidateException("新增用户'" . $user['userName'] . "'失败，登录账号已存在");
        } else if (!empty($user['userName']) && $this->checkUserNameUnique($user['userName'], null)) {
            throw new ValidateException("新增用户'" . $user['userName'] . "'失败，手机号码已存在");
        }
        $salt = createPasswordSalt();
        $password = createPassword($salt, $user['password']);
        $user['salt'] = $salt;
        $user['password'] = $password;
        return $this->save($user);
    }

    public function checkPhoneUnique(string $phone, string|null $userId)
    {
        $query = $this->where('phone', $phone);

        if ($userId) {
            $query = $query->where('id', '<>', $userId);
        }

        return $query->count() > 0;
    }

    public function checkUserNameUnique(string $userName, string|null $userId)
    {
        $query = $this->where('userName', $userName);

        if ($userId) {
            $query = $query->where('id', '<>', $userId);
        }

        return $query->count() > 0;
    }

    public function deleteUserById($userId)
    {
        if (UserConstantsEnum::SUPER_ADMIN_ID == $userId) {
            throw new ValidateException('不允许删除超级管理员用户');
        }
        return self::destroy(function ($query) use ($userId) {
            $query->where('id', $userId);
        });
    }

    public function deleteUserByIds($userIds)
    {
        if (in_array(UserConstantsEnum::SUPER_ADMIN_ID, $userIds)) {
            throw new ValidateException('不允许删除超级管理员用户');
        }
        return self::destroy(function ($query) use ($userIds) {
            $query->whereIn('id', $userIds);
        });
    }

    //-----------------------------------------------------------------------------------
    //｜                                   模型关联                                       ｜
    //-----------------------------------------------------------------------------------

    public function roleInfo()
    {
        return $this->hasOne(SysRole::class, 'id', 'roleId');
    }

}